<?php

class WechatPaySignature
{
    /**
     * 生成微信支付V3 API签名
     *
     * @param string $method HTTP请求方法
     * @param string $url 请求URL（不包含域名）
     * @param string $timestamp 时间戳
     * @param string $nonceStr 随机字符串
     * @param string $body 请求报文主体
     * @param string $privateKey 商户API私钥
     * @return string Base64编码的签名
     */
    public static function generateSignature(
        string $method,
        string $url,
        string $timestamp,
        string $nonceStr,
        string $body,
        string $privateKey
    ): string {
        // 构造签名串
        $signatureString = sprintf("%s\n%s\n%s\n%s\n%s\n",
            $method,
            $url,
            $timestamp,
            $nonceStr,
            $body
        );
        
        // 使用私钥进行SHA256 with RSA签名
        if (!openssl_sign($signatureString, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
            throw new RuntimeException("签名失败: " . openssl_error_string());
        }
        
        // 对签名结果进行Base64编码
        return base64_encode($signature);
    }

    /**
     * 验证签名
     *
     * @param string $signature 待验证的签名（Base64编码）
     * @param string $method HTTP请求方法
     * @param string $url 请求URL
     * @param string $timestamp 时间戳
     * @param string $nonceStr 随机字符串
     * @param string $body 请求报文主体
     * @param string $publicKey 公钥
     * @return bool 是否验证通过
     */
    public static function verifySignature(
        string $signature,
        string $method,
        string $url,
        string $timestamp,
        string $nonceStr,
        string $body,
        string $publicKey
    ): bool {
        $signatureString = sprintf("%s\n%s\n%s\n%s\n%s\n",
            $method,
            $url,
            $timestamp,
            $nonceStr,
            $body
        );
        
        $decodedSignature = base64_decode($signature);
        
        return openssl_verify(
            $signatureString,
            $decodedSignature,
            $publicKey,
            OPENSSL_ALGO_SHA256
        ) === 1;
    }

    /**
     * 生成Authorization头
     *
     * @param string $mchId 商户号
     * @param string $serialNo 证书序列号
     * @param string $nonceStr 随机字符串
     * @param string $timestamp 时间戳
     * @param string $signature 签名
     * @return string Authorization头
     */
    public static function generateAuthHeader(
        string $mchId,
        string $serialNo,
        string $nonceStr,
        string $timestamp,
        string $signature
    ): string {
        return sprintf(
            'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",signature="%s",timestamp="%s",serial_no="%s"',
            $mchId,
            $nonceStr,
            $signature,
            $timestamp,
            $serialNo
        );
    }
}

// 示例参数
$merchantId = '1900007291'; // 商户号
$serialNo = '408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB'; // 证书序列号
$method = 'GET'; // HTTP方法
$url = '/v3/refund/domestic/refunds/123123123123'; // 请求URL
$timestamp = '1554208460'; // 时间戳
$nonceStr = '593BEC0C930BF1AFEB40B4A08C8FB242'; // 随机字符串
$body = ''; // 请求体

// 示例私钥（实际使用时应该从文件中读取）
$privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAptpm+qvIDCh/9wjU26SQCK26ogYkBhDrYxnAaw2JbbBsp1oD
bHKk+1r381NeBUG2HEFAuU+Fr72u5ot3yKdzoF/FajAzQNKnm569/D3upKoi8mYB
aST15Uig8j8qoUW1U217LL0jEHlSnHV3lcaDTXqDpTRR4Bfz9IqOgJgFZ8/oTfEo
mSrjrLYef81Eyxr7ZIMQXEKKEK7V4UXKS0+/fDsiG/cXidhzt8UbTL9vqXqxM2+I
DImyO+FAc/tkBG55LmzxPto1Nq0WbnZzRM/wTzrd0I/8NlevxtFbphg4evlHjFNI
7+GrqR87ViEwuAJJ9Je5QQjct5YJfFRWiZ5CMQIDAQABAoIBAGBi/GhEgezcHIg1
ltlHaFlLGuxsRbUnYwM9phVxnXk7GJlYe2/TjpERjPkIqOC6hBwwadZjJORP3FCc
Mtc8PKRhjuZ377O7vU0915x2nnyLOGL1IE2AJ3iLi0ZFzTea0FPgg+5lWHM00s9F
YI6qPcGtS41M+xtMWwZiYE3TBBRibHiY8ugGyaNAhiMKehyW05uApjlIF55wwCGx
BkyESJpGRR/6853iHke6Ge+xVcMa9QmQdoH0QqL/8kT28PL568mJJr0Ow/83t4+d
Pe70YPzKAxgUnaDsHJqO+b8qH69AEs8rTI5h2Mon6pH+bJT66KUoiXhn+Kf+4LSs
henRP10CgYEA1QJSfuFOWVRjrg3N/rAIc/Ak84BTZavbyrkqBSuoTs9i/nMI/hOz
VxpDntg7Bx2Tctl6sZO3GioTxKdc/YYaTKci1TKBbeginpsqEQVgwkMCy8HpvUmR
fyAMqLwZC4h9+j+NiZtuoFJDTCgv+WYbasX+kWYEUM21bnSYuO7yEQsCgYEAyIdP
r9uzqPgzN34Tmx+CNTa16VjhBh+zkBtXRLDLhWBeIYxoYNJARD98Pb1XZdvpkZZW
Sk7MfaKo2/DomzyyyB/MbHWwAdFi3yb4y7uMJfyC1MzdUSNN3Vp579hJxHkJ+nN4
Ys76yfcEeVOLnvUT1Z0KKCdIWRdT1Lgi+X1itzMCgYBJUXlPzwGG4fNFj97d0X23
Wmt9nSgXkOYgi0eZbAOMzPmIF9R6kBFk49dur4Lx2g5Ms+r1gKC/0sfnIqxxX11i
EQ1+UNoYGJUB/uql3TIG68XkmKR50P7RwRhaZBRC0gJ6xrFTMjsL2ATuC88niyvY
vrn3FiRaI9RVZrDCxwxvLQKBgEXW4okEAqGBuAzGqztmkOnJoTehDdYdKmOxMgap
cGiGdKJIjX3THDDoz3ONQyglnEZpTqpYoV3MTfU0BT8zt6x9bqwDnQY1D7NalmIW
cqw0Mri8lQQSQKcsQLWo5aA466G/n5kCL1Qx5OwAjesRvhOyuvvbGpZ0ymyWqQ+t
fLkDAoGATcul1L8y5D/wNVP1GXbXMZfBsFP3bbqy8c+Ashm6g8OLm2mGNntd5Z6h
1KkID7Yksh+dZ6t7XaPBtGACXX5Eryr537JVvdX8hAVCp5HVtaN/9VBVP8Ka2e4s
VS/xeNgOMQ7uzhRPBJ8HiTmdI1nHhDnYQpGiBgQn0Z5RAkSvFMk=
-----END RSA PRIVATE KEY-----
EOD;

try {
    // 1. 计算签名
    $signature = WechatPaySignature::generateSignature(
        $method,
        $url,
        $timestamp,
        $nonceStr,
        $body,
        $privateKey
    );
    
    echo "生成的签名值: \n" . $signature . "\n\n";
    
    // 2. 生成Authorization头
    $authHeader = WechatPaySignature::generateAuthHeader(
        $merchantId,
        $serialNo,
        $nonceStr,
        $timestamp,
        $signature
    );
    
    echo "生成的Authorization头: \n" . $authHeader . "\n\n";
    
    // 3. 验证签名（可选）
    // 从私钥中提取公钥
    $privateKeyResource = openssl_pkey_get_private($privateKey);
    $publicKey = openssl_pkey_get_details($privateKeyResource)['key'];
    
    $isValid = WechatPaySignature::verifySignature(
        $signature,
        $method,
        $url,
        $timestamp,
        $nonceStr,
        $body,
        $publicKey
    );
    
    echo "签名验证结果: " . ($isValid ? "成功" : "失败") . "\n";
    
    // 4. 示例CURL请求（需要实际API地址）
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://api.mch.weixin.qq.com' . $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: ' . $authHeader,
        'Accept: application/json',
        'User-Agent: YourApp/1.0'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
    // 执行请求（实际使用时取消注释）
    // $response = curl_exec($ch);
    // if (curl_errno($ch)) {
    //     echo 'CURL错误: ' . curl_error($ch);
    // } else {
    //     echo "\nAPI响应: \n" . $response . "\n";
    // }
    // curl_close($ch);
    
} catch (Exception $e) {
    echo "发生错误: " . $e->getMessage() . "\n";
}
?>